home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PsL Monthly 1993 December
/
PSL Monthly Shareware CD-ROM (December 1993).iso
/
prgmming
/
dos
/
pascal
/
tp_asm.exe
/
lha
/
TPA&RTL.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1989-10-16
|
9KB
|
260 lines
(**** TPA&RTL Documentation and Demonstration ****
TP&Asm Version 2.2 (Revision 1)
This revision can be distinguished from the original Version 2.2 by
the 2:21 time stamp and by the Version number shown in the copyright
message. In most of the documentation, however, I will continue to
refer to this revision as "Version 2.2".
TP&Asm Version 2.21 contains a small but interesting upgrade to the
TP&Asm program file which permits you to call any routine in the
Turbo Pascal RunTime Library directly from your TP&Asm assembly code.
(You do not need to purchase the RTL source license).
To use this feature you must determine the System Unit internal hex
code which designates the appropriate routine (more on this below),
and declare a standard Pascal constant equal to $C0DE0000 plus this
code. You may then specify the constant identifier as the target of
any assembly Call (or JMP) statement, and TP&Asm will link in the
designated RunTime Library routine.
For example, the internal hex code for the RunTime Library "LMod"
function is $0018. The following code fragment will compute
L1 Mod L2 and leave the result in (Bx,Cx)
CONST LMod = $C0DE0018;
:
Assembly
Les Ax,L1
Mov Dx,Es ;load L1 into (Dx,Ax)
Les Cx,L2
Mov Bx,Es ;load L2 into (Bx,Cx)
Call LMod ;call RTL routine which leaves result in (Bx,Cx)
:
All valid internal hex codes are multiples of 4, and the following
limits are enforced:
VER40: $0004 to $0260
VER50: $0004 to $0284
VER55: $0004 to $0294
I don't guarantee that all codes up to these maximum values are valid
RTL routines, however I am fairly certain that codes beyond these are
not valid.
Please note:
I have made no attempt to explain or even determine what all of the
RunTime Library routines do nor how they should be called. I have
left that to the user. In many cases the following technique can
be applied to determine the internal code of a particular RunTime
Library routine:
1) create a file which defines the entire range of RTL constants
and makes one call to each, and which also contains a single
Pascal line which will invoke the desired RTL routine:
Program FindRTL;
CONST
RTL0004 = $C0DE0004;
RTL0008 = $C0DE0008;
: :
RTL0294 = $C0DE0294; {- v5.5 Maximum -}
VAR
L1,L2,L3: LongInt;
BEGIN
L3 := L1 Mod L2; {- invoke RTL LMod function -}
Assembly
Call RTL0004
Call RTL0008
:
Call RTL0294
End; {Assembly}
END.
2) compile and create a map file, eg: "tpa c FindRTL/gd". Then use
EXAMINE.EXE (available in the archive TP-XMN) to examine the
compiled file and save the output ("examine FindRTL >rtl.xmn").
3) Edit the examine output file, find the Far Call(s) associated
with the Pascal statement, and locate the assembly Call(s) which
reference the same location. The relevant parts of the example
in (1) are shown below:
VAR
L1,L2,L3: LongInt;
BEGIN
3FC1:0000 9A0000F83F CALL 3FF8:0000
3FC1:0005 55 PUSH BP
3FC1:0006 89E5 MOV BP,SP
----
L3 := L1 Mod L2; {- invoke RTL LMod function -}
3FC1:0008 C4063E00 LES AX,[003E]
3FC1:000C 8CC2 MOV DX,ES
3FC1:000E C40E4200 LES CX,[0042]
3FC1:0012 8CC3 MOV BX,ES
3FC1:0014 9A9402F83F CALL 3FF8:0294 ** This is the RTL Call **
3FC1:0019 89C8 MOV AX,CX
3FC1:001B 89DA MOV DX,BX
3FC1:001D A34600 MOV [0046],AX
3FC1:0020 89164800 MOV [0048],DX
----
:
:
----
Call RTL0018
3FC1:0040 9A9402F83F CALL 3FF8:0294 ** Matching Assembly Call **
----
Call RTL001C
3FC1:0045 9A0203F83F CALL 3FF8:0302
----
:
It has been my observation that many of the RunTime Library routines
have retained the same internal code for all versions (4.0 and above),
however I am not certain that that will be true in all cases. In some
cases, for example RtlGetMem and RtlFreeMem shown below, the internal
code has remained the same but the calling conventions have changed.
The example below is designed to provide a simple demonstration of
the use of assembly language RTL calls. The RunTime Library FreeMem
pocedure is used to implement a "ShrinkMem" capability. A typical
use of ShrinkMem would be to read a data file into a large buffer on
the heap, and then to reduce the allocated heap space to the amount
actually required:
GetMem(DataBuf,64000);
Assign(DataFile,DataPath); Reset(DataFile,1);
BlockRead(DataFile,DataBuf^,64000,BytesRead);
ShrinkMem(DataBuf,64000,BytesRead);
An additional example is provided by the UserRunError procedure,
which demonstrates one of the relatively rare circumstances where
it is useful to make an unconditional JMP to a procedure.
Unlike the Version 5.x "RunError" procedure which flags the address
of the RunError call itself, this procedure provides a more useful
function by flagging the line which invoked the routine containing
the UserRunError call. Thus,
ShrinkMem(p1,21000,30000); {- RunTime Error 204 -}
will generate a RunTime Error at this line itself, not at some
obscure point within ShrinkMem where the error was detected. If
ShrinkMem resides in a unit which was compiled without debug
information ({$D-}), or for which source code is not available,
UserRunError (but not v 5.x RunError) will still correctly report
the address of the invalid ShrinkMem Call.
ShrinkMem could have been written entirely in Pascal; the use of
assembly language simply makes it smaller and faster. Note that
it could not be written in inline or External assembly language,
however - unless you have purchased the RTL license, recompiled
the System unit with all of the routines "interfaced", and then
recompiled all of your standard and user-defined units to recognize
the recompiled System unit.
I believe that UserRunError is an example of a procedure which
could not be written either in Pascal or inline/External assembly
(again, unless you have the RTL license and recompile all your
units).
This example file can be compiled with TP&Asm Version 2.21 running
Turbo Pascal Version 4.0, 5.0, or 5.5.
*****************************************************************)
{$M $1000,51000,51000}
CONST
RtlRunError = $C0DE0008;
RtlGetMem = $C0DE0088;
RtlFreeMem = $C0DE008C;
VAR
p1,p2,p3: POINTER;
{$F+} PROCEDURE RunError(ErrorNo: WORD); Forward; {$F-}
{$F+} PROCEDURE UserRunError(ErrorNo: WORD); Forward; {$F-}
Internal RTE
RunError Proc Far ; (for version 4.0 users)
Mov Bx,Sp
Ss Mov Ax,[Bx+4] ;load ErrorNo into Ax
Jmp RtlRunError ;Jmp to Runtime Library Routine
RunError ENDP
UserRunError Proc Far
Pop Ax,Ax,Ax ;Clear Return Cs:Ip and load ErrorNo into Ax
Mov Sp,Bp ;Proc/Function containing UserRunError
Pop Bp ; Must have standard Proc/Function entry code
Jmp RtlRunError ; (and must be FAR)
UserRunError ENDP
End
{$F+} PROCEDURE ShrinkMem(PVar: POINTER; OldSize,NewSize: WORD); {$F-}
{$S-} BEGIN {$S+}
Assembly
Les Ax,PVar ;set Dx:Ax
Mov Dx,Es ; to PVar
Add Ax,NewSize ;Point to "PVar+NewSize"
Mov Bx,Ax ;Normalize
And Ax,000Fh ; Pointer
Shr Bx,4 ; before passing
Add Dx,Bx ; to FreeMem
Mov Bx,OldSize ;Compute
Sub Bx,NewSize ; amount to free
jA Valid
Pas UserRunError(204);
Valid:
Pas {$IFDEF VER55} {- 5.5 uses pointer Value -}
Push Dx,Ax,Bx ;Push parameters for FreeMem
Pas {$ELSE} {- 4.0, 5.0 use pointer Address -}
Mov W PVar,Ax
Mov W PVar+2,Dx
Lea Ax,PVar
Push Ss,Ax,Bx ;Push parameters for FreeMem
Pas {$ENDIF}
Call RtlFreeMem ;Call RunTime Library Routine
End {Assembly}
END;
PROCEDURE ShowMaxAvail;
BEGIN WRITELN('Size of largest available block is ',MaxAvail); END;
BEGIN {- Verify proper operation of Shri